package com.lafabrick.components
{
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	
	import mx.containers.Canvas;
	import mx.core.IContainer;
	import mx.core.UIComponent;
	import mx.events.ResizeEvent;
	
	[IconFile("AnchorLayoutIcon.png")]
	
	/**
	 * Couleur des règles.
	 */
	[Style(name="rulesColor", format="Color", type="Color", inherit="no")]
	
	/**
	 * Epaisseur des règles, en pixel.
	 */
	[Style(name="rulesThickness", format="Number", inherit="no")]
	
	/**
	 * Transparence des règles.
	 */
	[Style(name="rulesAlpha", format="Number", inherit="no")]
	
	/**
	 * Couleur de la grille.
	 */
	[Style(name="gridColor", format="Color", type="Color", inherit="no")]
	
	/**
	 * Epaisseur de la grille, en pixel.
	 */
	[Style(name="gridThickness", format="Number", inherit="no")]
	
	/**
	 * Transparence de la grille.
	 */
	[Style(name="gridAlpha", format="Number", inherit="no")]
	
	
	/**
	 * Composant étendu de Canvas. 
	 * Possibiliter d'ancrer ses enfants (par l'écoute des évènenement startDragging et stopDragging du composants DraggableWindow).
	 */
	public class AnchorLayout extends Canvas
	{
		
		private var _approach:Number = 20;
		private var _gap:Number = 10;
		
		private var _showRules:Boolean = true;
		private var _rulesColor:uint = 0xFFFFFF;
		private var _rulesThickness:Number = 1;
		private var _rulesAlpha:Number = 1;
		
		private var _showGrid:Boolean = false;
		private var _gridDistance:Number = 10;
		private var _gridColor:uint = 0xFFFFFF;
		private var _gridThickness:Number = 1;
		private var _gridAlpha:Number = 0.5;
		
		private var _snapToGrid:Boolean = false;
		
		private var _lastPosX:Number;
		private var _lastPosY:Number;
		
		private var _anchorRect:Rectangle;
		
		private var _rulesShape:UIComponent = new UIComponent();
		private var _gridShape:UIComponent = new UIComponent();
		
		//private var _clickEffect:BitmapFilter = new GlowFilter(0x000000, 1, 20, 20, 1, 3);
		private var _clickEffect:Array = [];
		private var _lastChildClick:DisplayObject;
		
		/**
		 * Constructeur
		 */
		public function AnchorLayout()
		{
			super();
			_gridShape.id = "_gridShape";
			_rulesShape.id = "_rulesShape";
			addChild(_gridShape);
			addChild(_rulesShape);
			horizontalScrollPolicy = "off";
			verticalScrollPolicy = "off";
			includeInLayout = true;
		}
		[Inspectable(type="Array", defaultValue=null, verbose=1, category="Anchor Properties")]
		public function set selectedChildFilter( af:* ) : void
		{
			if (af as Array) {
				_clickEffect = af;
			}
			else {
				_clickEffect = [af];
			}
		}
		
		/**
		 * Définit le (ou les) filtre(s) appliqué à l'enfant sélectionné.
		 * @param value Array ou BitmapFilter
		 * 
		 */
		public function get selectedChildFilter() : Array
		{
			return _clickEffect;
		}
		
		[Inspectable(type="Boolean", defaultValue=true, verbose=1, category="Anchor Properties")]
		public function set showRules( b:Boolean ) : void
		{
			_showRules = b;
		}
		
		/**
		 * Définit si les règles sont visibles ou non (true / false).
		 * @param value:Booloean (défaut : true)
		 */
		public function get showRules() : Boolean
		{
			return _showRules;
		}
		
		[Inspectable(type="Boolean", defaultValue=false, verbose=1, category="Anchor Properties")]
		public function set showGrid( b:Boolean ) : void
		{
			_showGrid = b;
			if (_showGrid) {
				this.addEventListener(ResizeEvent.RESIZE, onAppResize);
				generateGrid();
			}
			else {
				this.removeEventListener(ResizeEvent.RESIZE, onAppResize);
				removeGrid();
			}
		}
		
		/**
		 * Définit si les règles sont visibles ou non (true / false).
		 * @param value:Booloean (défaut : true)
		 */
		public function get showGrid() : Boolean
		{
			return _showGrid;
		}
		
		[Inspectable(format="Length", type="Number", defaultValue=10, verbose=1, category="Anchor Properties")]
		public function set gridDistance( v:Number ) : void
		{
			_gridDistance = v;
			generateGrid();
		}
		
		/**
		 * Taille de la grille en pixel.
		 * @param value:Number (défaut : 10)
		 */
		public function get gridDistance() : Number
		{
			return _gridDistance;
		}
		
		[Inspectable(type="Boolean", defaultValue=false, verbose=1, category="Anchor Properties")]
		public function set snapToGrid( b:Boolean ) : void
		{
			_snapToGrid = b;
		}
		
		/**
		 * Définit si l'accroche à la grille est activée ou non (true / false).
		 * @param value:Booloean (défaut : true)
		 */
		public function get snapToGrid() : Boolean
		{
			return _snapToGrid;
		}
		
		// ----------
		[Inspectable(defaultValue=20, verbose=1, category="Anchor Properties")]
		public function set approach( n:Number ) : void
		{
			_approach = n;
		}
		/**
		 * Définit la distance (en pixel) d'approche entre 2 enfants.
		 * @param value:Number (défaut : 20)
		 */
		public function get approach() : Number
		{
			return _approach;
		}
		
		[Inspectable(defaultValue=10, verbose=1, category="Anchor Properties")]
		public function set gap( n:Number ) : void
		{
			_gap = n;
		}
		/**
		 * Définit la distance (en pixel) entre 2 enfants une fois ceux-ci collés.
		 * @param value:Number (défaut : 10)
		 */
		public function get gap() : Number
		{
			return _gap;
		}
		
		private var _dragInitiator:DisplayObject;
		
		private function onChildClick( e: MouseEvent ) : void
		{
			if (_lastChildClick != null) {
				_lastChildClick.filters = null;
			}
			
			//trace(e.currentTarget.id + " | "+_gridShape.id);
			if (e.currentTarget.id != _gridShape.id) {
				e.currentTarget.filters = _clickEffect;
				_lastChildClick = e.currentTarget as DisplayObject;
				setChildIndex(_lastChildClick, numChildren - 1);
			}
			//setChildIndex(_lastChildClick, numChildren - 1);
			
		}
		
		private function onChildStartDragging( e : Event ) : void
		{
			
		}
		
		private function onChildIsDragging( e : Event ) : void
		{
			_dragInitiator = e.currentTarget as DisplayObject;
			
			if(_snapToGrid) 
			{
				_dragInitiator.x += _gridDistance - _dragInitiator.x%_gridDistance
				_dragInitiator.y += _gridDistance - _dragInitiator.y%_gridDistance
			}
			
			execute(_dragInitiator, this);
			
		}
		
		
		private function onChildStopDragging( e : Event ) : void
		{
			var g:Graphics = _rulesShape.graphics;
			g.clear();
		}
		
		private var _tweenInitiator:DisplayObject = null;
		
		private function execute(dragInitiator:DisplayObject, parent:IContainer) : void
		{
			
			var rect:Rectangle = dragInitiator.getRect(parent as DisplayObject);
			
			var dist:Number;
			
			var i:int = 0;
			
			var g:Graphics = _rulesShape.graphics;
			g.clear();
			
			// Pour le anchorLayout
			// DROITE INTERIEUR
			if ( this.width > rect.left) {
				dist = this.width - rect.right;
				if ( dist >= 0 && dist <= _approach) {	
					dragInitiator.x = this.width - rect.width;	
				}
			}
			
			// GAUCHE INTERIEUR
			if (0 < rect.right) {
				dist = 0 - rect.left;	
				if ( dist >= 0 && dist <= _approach) {					
					dragInitiator.x = 0;
				}
			}
			
			// TOP INTERIEUR
			if (0 < rect.bottom) {
				dist = - rect.top;
				if ( dist >= 0 && dist <= _approach) {
					dragInitiator.y = 0;
				}
			}
			
			//BAS INTERIEUR
			if (rect.top < this.height) {
				dist = rect.bottom - this.height;
				if ( dist >= 0 && dist <= _approach) {	
					dragInitiator.y = this.height - rect.height;
				}
			}
			
			while(i<parent.numChildren) {
				
				var child:DisplayObject = parent.getChildAt(i++);
				if (child == dragInitiator) continue;
				
				var r:Rectangle = child.getRect(parent as DisplayObject);
				
				// DROITE
				if (r.right < rect.left) {
					dist = rect.left - r.right;
					if ( dist >= 0 && dist <= _approach) {
						
						dragInitiator.x = r.right + _gap;
						
						if (_showRules) {
							g.lineStyle(_rulesThickness, _rulesColor, _rulesAlpha);
							g.moveTo(r.right + _gap/2, 0);
							g.lineTo(r.right + _gap/2, this.height);
							g.endFill();
						}		
					}
					
				}
				
				
				// DROITE INTERIEUR
				else if (r.right > rect.left) {
					dist = r.right - rect.right;
					
					if ( dist >= 0 && dist <= _approach) {
						
						dragInitiator.x = r.right - rect.width;	
						
						if (_showRules) {
							g.lineStyle(_rulesThickness, _rulesColor, _rulesAlpha);
							g.moveTo(r.right + _gap/2, 0);
							g.lineTo(r.right + _gap/2, this.height);
							g.endFill();
						}
					}
				}
				
				// GAUCHE
				if (r.left > rect.right) {
					dist = r.left - rect.right;
					
					if ( dist >= 0 && dist <= _approach) {
					
						dragInitiator.x = r.left - rect.width - _gap;
						
						if (_showRules) {
							g.lineStyle(_rulesThickness, _rulesColor, _rulesAlpha);
							g.moveTo(r.left - _gap/2, 0);
							g.lineTo(r.left - _gap/2, this.height);
							g.endFill();
						}
					}
				}
				
				// GAUCHE INTERIEUR
				if (r.left < rect.right) {
					dist = r.left - rect.left;
					
					if ( dist >= 0 && dist <= _approach) {
						
						dragInitiator.x = r.left;
					
						if (_showRules) {
							g.lineStyle(_rulesThickness, _rulesColor, _rulesAlpha);
							g.moveTo(r.left - _gap/2, 0);
							g.lineTo(r.left - _gap/2, this.height);
							g.endFill();
						}
					}
				}
				
				//HAUT
				if (r.top > rect.bottom) {
					dist = r.top - rect.bottom;
					
					if ( dist >= 0 && dist <= _approach) {
						
						dragInitiator.y = r.top - rect.height - _gap;	
						
						if (_showRules) {
							g.lineStyle(_rulesThickness, _rulesColor, _rulesAlpha);
							g.moveTo(0, r.top - _gap/2);
							g.lineTo(this.width, r.top - _gap/2);
							g.endFill();
						}
					}
				}
				 
				//HAUT INTERIEUR
				if (r.top < rect.bottom) {
					dist = r.top - rect.top;
					
					if ( dist >= 0 && dist <= _approach) {
						
						dragInitiator.y = r.top;
						
						if (_showRules) {
							g.lineStyle(_rulesThickness, _rulesColor, _rulesAlpha);
							g.moveTo(0, r.top - _gap/2);
							g.lineTo(this.width, r.top - _gap/2);
							g.endFill();
						}	
					}
				}
				
				//BAS
				if (rect.top > r.bottom) {
					dist = rect.top - r.bottom;
					
					if ( dist >= 0 && dist <= _approach) {
						
						dragInitiator.y = r.bottom + _gap;
						
						if (_showRules) {
							g.lineStyle(_rulesThickness, _rulesColor, _rulesAlpha);
							g.moveTo(0, r.bottom + _gap/2);
							g.lineTo(this.width, r.bottom + _gap/2);
							g.endFill();
						}
					}
				}
				
				//BAS INTERIEUR
				if (rect.top < r.bottom) {
					dist = rect.bottom - r.bottom;
					
					if ( dist >= 0 && dist <= _approach) {
						
						dragInitiator.y = r.bottom - rect.height;
						
						if (_showRules) {
							g.lineStyle(_rulesThickness, _rulesColor, _rulesAlpha);
							g.moveTo(0, r.bottom + _gap/2);
							g.lineTo(this.width, r.bottom + _gap/2);
							g.endFill();
						}
					}
				}

				_lastPosX = dragInitiator.x;
				_lastPosY = dragInitiator.y;
				
			}
		}
		
		/**
		 * @private 
		 */
		private function onAppResize( e:ResizeEvent ) : void
		{
			_anchorRect = this.getRect(parent as DisplayObject);
			trace(_anchorRect);
			if(_showGrid) {
				generateGrid();
			}
		}
		
		/**
		 * @private
		 */
		private function generateGrid() : void
		{
			var g:Graphics = _gridShape.graphics;
			
			if(_showGrid) {
				g.clear();
				
				var gx:Number;
				var gy:Number;
				
				for (var i:Number = 0; i<this.width/_gridDistance; i++) {
					// horizontale
					gx = _gridDistance + i*_gridDistance;
					gy = _gridDistance + i*_gridDistance;
					
					g.lineStyle(_gridThickness, _gridColor, _gridAlpha);
					g.moveTo(gx, 0);
					g.lineTo(gx, this.height);
					g.endFill();
					
					// verticale
					g.lineStyle(_gridThickness, _gridColor, _gridAlpha);
					g.moveTo(0, gy);
					g.lineTo(this.width, gy);
					g.endFill();
				}
			}
			
			_gridShape.cacheAsBitmap = true;
		}
		
		/**
		 * @private
		 */
		private function removeGrid() : void
		{
			var g:Graphics = _gridShape.graphics;
			g.clear();
		}
		
		/**
		 * @private
		 */
		override public function addChild(child:DisplayObject):DisplayObject
		{
			child.addEventListener(MouseEvent.MOUSE_DOWN, onChildClick);
			
			if (child is DraggableWindow) {
				child.addEventListener("startDragging", onChildStartDragging);
				child.addEventListener("isDragging", onChildIsDragging);
				child.addEventListener("stopDragging", onChildStopDragging);	
			}
			
			return super.addChild(child);
		}
		
		/**
		 * @private
		 */
		override public function removeChild(child:DisplayObject):DisplayObject
		{
			
			if (child is DraggableWindow) {		
				child.removeEventListener("startDragging", onChildStartDragging);
				child.removeEventListener("isDragging", onChildIsDragging);
				child.removeEventListener("stopDragging", onChildStopDragging);
			}
			
			child.removeEventListener(MouseEvent.MOUSE_DOWN, onChildClick);
			
			return super.removeChild(child);
		}
		
		/**
		 * @private
		 */
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{	
			
			_rulesColor = getStyle("rulesColor") != null ? getStyle("rulesColor") : _rulesColor;
			_rulesThickness = getStyle("rulesThickness") != null ? getStyle("rulesThickness") : _rulesThickness;
			_rulesAlpha = getStyle("rulesAlpha") != null ? getStyle("rulesAlpha") : _rulesAlpha;

			_gridColor = getStyle("gridColor") != null ? getStyle("gridColor") : _gridColor;
			_gridThickness = getStyle("gridThickness") != null ? getStyle("gridThickness") : _gridThickness;
			_gridAlpha = getStyle("gridAlpha") != null ? getStyle("gridAlpha") : _gridAlpha;
			
			
			
			super.updateDisplayList(unscaledWidth, unscaledHeight);
			
		}
		
		/**
		 * @private
		 */
		override public function initialize(): void 
		{
            super.initialize();
            _anchorRect = this.getRect(parent as DisplayObject);
			
  		}
	}
}